/***************************************************************************
 *   Copyright (C) 2007 by Alexandre Masselot,,,   *
 *   alex@alex-laptop   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Library General Public License as       *
 *   published by the Free Software Foundation; either version 2 of the    *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this program; if not, write to the                 *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#ifndef BOLRUNLCMSMS_H
#define BOLRUNLCMSMS_H

#ifdef HAVE_BOOST_SERIALIZATION
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/utility.hpp>
#include <boost/serialization/vector.hpp>
#else
#include <vector>
#endif

#ifdef HAVE_PROTOBUF
#ifdef _MSC_VER
// we don't create DLLs and thus don't need to pay attention to this warning
#pragma warning( disable: 4251 )
#endif
#include "runlcmsms.pb.h"
#include "spectrumpeakrefdualptr.h"
#endif

#include "spectrumlcmsmscollection.h"
#include "spectrumlcmscollection.h"


namespace bol {

/**
  A collection of spectrum LC/MSMS

  @author Alexandre Masselot,,, <alex@alex-laptop>
 */
  class RunLcmsms
  {
    private:
      string m_description;
      SpectrumLcmsmsCollection m_msmsCollection;
      SpectrumLcmsCollection m_msCollection;

#ifdef HAVE_BOOST_SERIALIZATION
      // *** BOOST serialisation code ***
      friend class boost::serialization::access; // gives access to serialisator
      template<class Archive> void serialize(Archive & ar, const unsigned int version) {
        ar & boost::serialization::make_nvp("description", m_description );
        ar & boost::serialization::make_nvp("msCollection", m_msCollection );
        ar & boost::serialization::make_nvp("msmsCollection", m_msmsCollection );
      }
#endif

    public:
      RunLcmsms();

      ~RunLcmsms();

      SpectrumLcmsmsCollection& msmsCollection() {
        return m_msmsCollection;
      }
      SpectrumLcmsCollection& msCollection() {
        return m_msCollection;
      }
      void buildIndex();

      void setDescription(const string& theValue) {
        m_description = theValue;
      }

      string description() const {
        return m_description;
      }
  };

#ifdef HAVE_PROTOBUF
  // NOTE order is important : we must have done MS before MS^2 so ref have where to point
  static inline bool operator<< (protobuf::RunLcmsms& pb, class RunLcmsms& b) {
    pb.set_description(b.description());

    // Do we do MS^2-to-MS references ?
    if (! (b.msCollection().size() && b.msmsCollection().size()) ) { // we need both a MS and MS^2 collection to do the links
      // simply load the collections into protobuf serialiser
      pb << b.msCollection();
      pb << b.msmsCollection();
    } else {
      // data structure to help finding indices faster 
      // (map do binary search and are O(log n) )
      Ptr2IndexMapPtr colidx (new Ptr2IndexMap); // index for whole collection
      vector<Ptr2IndexMapPtr> spidx; // For each spectrum, this vector holds 1 map (for peaks)

      // load the collections into protobuf serialiser
      // (and keep track of which index represents which pointer)
      protobufwithmap(pb, b.msCollection(), colidx, spidx);
      pb << b.msmsCollection();

      // ok now, that both collection are loaded into the serialiser
      // use Captain-Bol's pointers (and the map) to put correct references into protobuf serialiser
      int j;
      SpectrumLcmsmsCollection::const_iterator i;

      for (j=0, i = b.msmsCollection().begin(); i != b.msmsCollection().end(); i++, j++) {
        if ((*i)->spectrumPeakRef() != NULL) {

          // find the spectrum
          const void* ptr = static_cast<const void*>((*i)->spectrumPeakRef()->spectrum());
          if (ptr!= NULL) { // has a reference ?
            Ptr2IndexMap::iterator where = colidx->find(ptr);
            if (where != colidx->end()) {
              int k = where->second;
              pb.mutable_ms2(j)->mutable_reference()->set_spectrum(k);

              // now find the peak
              const void* ptr = static_cast<const void*>((*i)->spectrumPeakRef()->peak());
              if (ptr!= NULL) { // has a reference ?
                Ptr2IndexMap::iterator where = spidx[k]->find(ptr);
                if (where != spidx[k]->end()) {
                  pb.mutable_ms2(j)->mutable_reference()->set_peak(where->second);
                }
              } // else : reference is already set to -1

            }
          } // else : reference is already set to -1

        } // else : reference is already cleared
      }
    }

    // smart pointers take care of the clean up
    return true;
  }

  static inline bool operator>> (const protobuf::RunLcmsms& pb, class RunLcmsms& b) {
    b.setDescription(pb.description());
    pb >> b.msCollection();
    pb >> b.msmsCollection();

     // Do we do MS^2-to-MS references ?
    if (b.msCollection().size() && b.msmsCollection().size()) { // we need both a MS and MS^2 collection to do the links
      // ok now, that both collection are unpacked out of the serialiser
      // use the indices in protobuf serialiser to point to corresponding Captain-Bol objects
      for (int i = 0; i < pb.ms2_size(); i++) {
        if (pb.ms2(i).has_reference()) {
          // find spectrum
          Spectrum<PeakIntensity*>* sptr =
            (pb.ms2(i).reference().has_spectrum()
              && (pb.ms2(i).reference().spectrum() >= 0)
              && (pb.ms2(i).reference().spectrum() < b.msCollection().size()))
            ? b.msCollection()[ pb.ms2(i).reference().spectrum() ]
            : NULL;

          // find peak
          PeakIntensity* pptr =
            ((sptr != NULL) && pb.ms2(i).reference().has_peak()
              && (pb.ms2(i).reference().peak() >= 0)
              && (pb.ms2(i).reference().peak() < sptr->size()))
              ? (*sptr)[ pb.ms2(i).reference().peak() ]
            : NULL;

          // if there's a reference, add it to the spectrum
          if ((sptr!=NULL) || (pptr!=NULL)) {
            SpectrumPeakRefPtr ptr (new SpectrumPeakRefDualPtr<PeakIntensity> (sptr, pptr));
            b.msmsCollection()[i]->setSpectrumPeakRef(ptr);
          }
        }
      }
    }

    return true;
  }
#endif

}

// There isn't need to track "RunLcmsms" - because we'll serialise 1 whole object in 1 archive, so ne need to detect redundancy
// (unlike, for example, "peaks" which could get mentionned in several different place)
#ifdef HAVE_BOOST_SERIALIZATION
BOOST_CLASS_TRACKING(bol::RunLcmsms, boost::serialization::track_never)

#if defined(WIN32) && (BOOST_VERSION < 103600)
// other wise Visual Studio spits error messages
#include <boost/serialization/extended_type_info_typeid.hpp>
#endif
 
BOOST_CLASS_EXPORT_GUID(bol::RunLcmsms, "RunLcmsms") // register GUID
#endif

#endif
